#include "qcommunicate.h"

#include "win_serial.h"
#include "qmissionmanager.h"
#include "qmission.h"

QCommunicate::QCommunicate(QObject *parent) :
    QObject(parent),
    m_serial(NULL),
    m_mission_manager(NULL),
    m_timer(new QTimer),
    m_commu_status(CS_OFFLINE),
    m_serial_status(SS_CLOSED),
    m_trend_status(ETS_NONE)
{
    connect(m_timer,SIGNAL(timeout()),this,SLOT(timer()));
}

QCommunicate::~QCommunicate()
{
    closed();
    delete m_timer;
}

bool QCommunicate::open(const tagSerialConfig &config)
{
    if(m_serial_status==SS_OPEN)
    {
        if(m_config!=config)
        {
            closed();
        }
        else
        {
            if(m_commu_status==CS_PAUSE)
            {
                run();
            }
            return true;
        }
    }

    m_config=config;

    PortSettings serial_config;

    serial_config.FlowControl=FLOW_OFF;
    serial_config.Timeout_Millisec=500;

    if(config.m_stop==1)
    {
        serial_config.StopBits=STOP_1;
    }
    else if(config.m_stop==2)
    {
        serial_config.StopBits=STOP_2;
    }

    if(config.m_baud_rate==9600)
    {
        serial_config.BaudRate=BAUD9600;
    }
    else if(config.m_baud_rate==4800)
    {
        serial_config.BaudRate=BAUD4800;
    }
    else if(config.m_baud_rate==19200)
    {
        serial_config.BaudRate=BAUD19200;
    }
    else if(config.m_baud_rate==38400)
    {
        serial_config.BaudRate=BAUD38400;
    }
    else if(config.m_baud_rate==115200)
    {
        serial_config.BaudRate=BAUD115200;
    }

    if(config.m_data_length==7)
    {
        serial_config.DataBits=DATA_7;
    }
    else if(config.m_data_length==8)
    {
        serial_config.DataBits=DATA_8;
    }

    if(config.m_parity=='n')
    {
        serial_config.Parity=PAR_NONE;
    }
    else if(config.m_parity=='o')
    {
        serial_config.Parity=PAR_ODD;
    }
    else if(config.m_parity=='e')
    {
        serial_config.Parity=PAR_EVEN;
    }

    m_serial = new Win_Serial(config.m_serial_number,serial_config,QSerialBase::EventDriven);

    if(m_serial->open(QIODevice::ReadWrite))
    {
        m_timer->start(15);
        connect(m_serial,SIGNAL(readyRead()),this,SLOT(recv_data()));

        m_mission_manager=new QMissionManager();
        connect(m_mission_manager,SIGNAL(online()),this,SLOT(machine_online()));
        connect(m_mission_manager,SIGNAL(offline()),this,SLOT(machine_offline()));
        m_serial_status=SS_OPEN;
        m_commu_status=CS_CONNECTING;
        return true;
    }
    else
    {
        delete m_serial;
        m_serial=NULL;
        return false;
    }
}

void QCommunicate::closed()
{
    m_serial_status=SS_CLOSED;
    m_commu_status=CS_OFFLINE;
    emit offline();
    m_timer->stop();
    m_config.m_serial_number=-1;

    if(m_serial!=NULL)
    {
        m_serial->close();
        delete m_serial;
        m_serial=NULL;
    }

    if(m_mission_manager!=NULL)
    {
        delete m_mission_manager;
        m_mission_manager=NULL;
    }
}

void QCommunicate::timer()
{
    if(m_mission_manager==NULL || m_serial==NULL)
    {
        return;
    }

    if(m_trend_status!=ETS_NONE)
    {
        if(m_trend_status==ETS_WATE_DATA)
        {
            if(m_last_time.msecsTo(QDateTime::currentDateTime())>m_trend_time_out)
            {
                emit trend_time_out();
                return;
            }
        }
        if(m_trend_status==ETS_RECV_DATA)
        {
            if(m_last_time.msecsTo(QDateTime::currentDateTime())>200)
            {
                emit trend_time_out();
                return;
            }
        }
        return;
    }

    m_mission_manager->clear_delete(m_current_mission_id);
    if(m_current_mission_id=="")
    {
        QMission *ms=m_mission_manager->get_need_to_do();

        if(ms!=NULL)
        {
            m_current_mission_id=ms->get_attribute(MISSION_UUID).toString();
            send_mission_data(ms);
        }
    }
    else
    {
        QMission* ms=m_mission_manager->get_mission(m_current_mission_id);
        if(ms!=NULL)
        {
            if(m_last_time.msecsTo(QDateTime::currentDateTime())>
                    ms->get_attribute(MISSION_TIME_OUT,500).toInt())
            {
                m_mission_manager->mission_done(ms,TIME_OUT_CODE);
                m_current_mission_id="";
            }
        }
        else
        {
            m_current_mission_id="";
        }
    }
}

void QCommunicate::recv_data()
{
    if(m_serial==NULL)
    {
        return;
    }
    QByteArray array=m_serial->readAll();

    if(array.size()==0)
    {
        return;
    }

    memcpy(m_recv_data+m_length,array.data(),array.size());

    if(m_trend_status!=ETS_NONE)
    {
        m_last_time=QDateTime::currentDateTime();
        m_length+=array.size();
        emit trend_recv(m_length);
        if(m_length>=8192)
        {
            emit trend_finish();
        }
        return;
    }


    QMission* ms=m_mission_manager->get_mission(m_current_mission_id);
    if(ms==NULL)
    {
        return;
    }

    if(m_length+array.size()>ms->get_attribute(RECV_MAX_LENGTH).toInt())
    {
        m_mission_manager->mission_done(ms,MISSION_ERROR);
        m_current_mission_id="";
        m_last_time=QDateTime::currentDateTime();
        return;
    }


    if(m_recv_data[0]!=ms->get_attribute(MACHINE_ADDRESS).toInt())
    {
        m_mission_manager->mission_done(ms,MISSION_ERROR);
        m_current_mission_id="";
        return;
    }

    m_length+=array.size();
    if((m_recv_data[1]&0x80)!=0 && m_length>=5)
    {
        ushort us=QMission::get_crc(m_recv_data,3);
        ushort us_crc=((m_recv_data[3]&0xff)<<8)+(m_recv_data[4]&0xff);
        if(us==us_crc)
        {
            int code=(m_recv_data[2]&0xff);
            m_mission_manager->mission_done(ms,code);
            m_current_mission_id="";
        }
        else
        {
            m_mission_manager->mission_done(ms,CRC_ERROR_CODE);
            m_current_mission_id="";
        }
        m_length=0;
    }
    else
    {
        if(m_length>=ms->get_recv_length())
        {
            ushort us=QMission::get_crc(m_recv_data,m_length-2);
            ushort us_crc=((m_recv_data[m_length-2]&0xff)<<8)+(m_recv_data[m_length-1]&0xff);
            if(us==us_crc)
            {
                memcpy(ms->get_recv_buffer(),m_recv_data,m_length);
                m_mission_manager->mission_done(ms,0);
                m_current_mission_id="";
            }
            else
            {
                m_mission_manager->mission_done(ms,CRC_ERROR_CODE);
                m_current_mission_id="";
            }
            m_length=0;
        }

    }

    m_last_time=QDateTime::currentDateTime();
}

void QCommunicate::send_mission_data(QMission *ms)
{
    ms->set_attribute(MISSION_STATUS,MS_DOING);
    m_last_time=QDateTime::currentDateTime();
    m_serial->write((char*)ms->get_send_buffer(),ms->get_send_length());
    m_length=0;
}

void QCommunicate::run()
{
    m_mission_manager->reset();
    m_current_mission_id="";
}

void QCommunicate::machine_online()
{
    if(m_commu_status!=CS_ONLINE)
    {
        m_commu_status=CS_ONLINE;
        emit online();
    }
}

void QCommunicate::machine_offline()
{
    m_commu_status=CS_PAUSE;
    emit offline();
}

tagSerialConfig QCommunicate::get_config()
{
    return m_config;
}

QMissionManager* QCommunicate::mission_manager()
{
    return m_mission_manager;
}

int QCommunicate::commu_status()
{
    return m_commu_status;
}

int QCommunicate::serial_status()
{
    return m_serial_status;
}

void QCommunicate::start_trend(int time_out)
{
    if(time_out<500)
    {
        time_out=500;
    }
    m_trend_time_out=time_out;
    m_trend_status=ETS_WATE_DATA;
    m_last_time=QDateTime::currentDateTime();
}

uchar* QCommunicate::get_trend_data()
{
    return m_recv_data;
}

void QCommunicate::stop_trend()
{
    m_trend_status=ETS_NONE;
    m_last_time=QDateTime::currentDateTime();
}
